/****************************************************************************//**
  \file srecParser.c

  \brief Implementation of real time srec parser.

  \author
    Atmel Corporation: http://www.atmel.com \n
    Support email: avr@atmel.com

  Copyright (c) 2008-2013, Atmel Corporation. All rights reserved.
  Licensed under Atmel's Limited License Agreement (BitCloudTM).

  \internal
  History:
    2/04/09 A. Khromykh - Created
*******************************************************************************/
/******************************************************************************
                   Includes section
******************************************************************************/
#include <stdbool.h>
#include "bootStructure.h"
#include "flashLoader.h"
#include "eepromLoader.h"
#include "uart.h"
#include "hardwareInit.h"
#include "timerInterval.h"
#include <avr/eeprom.h>
#include "bootloader.h"

/******************************************************************************
                   Prototypes section
******************************************************************************/
static void srecWaitForType(SrecordType_t *srecType);
static uint8_t srecWaitForAddress(uint8_t addressLength);
static bool srecParser(void);

//============================================================================

BootBuffer_t dataBuffer;
static uint32_t srecACK  = ACK;
static uint32_t srecNACK = NACK;
static uint32_t previousAddress = 0xFFFFFFFF;
static uint32_t handshakeConf = HANDSHAKE_CONF;
uint32_t code_size;

/*
#if  defined(ATMEGA256RFR2) &&  !(defined(__IAR_SYSTEMS_ICC__)) 
volatile static uint32_t AppEntryPoint = 0x00000000;
#endif
*/
/******************************************************************************
                    Implementation section
******************************************************************************/
/**************************************************************************//**
\brief Waiting for a field of the type from the serial interface.

\param[out]
  srecType - pointer to buffer;
******************************************************************************/
static void srecWaitForType(SrecordType_t *srecType)
{
	do
	{
		UART1_GetByteWait((uint8_t *)srecType);
	} 
	while(S_SYMBOL != (uint8_t)*srecType);
	
	*srecType <<= 8;
	UART1_GetByteWait((uint8_t *)srecType);
}

/**************************************************************************//**
\brief Waiting for a field of the type from the internal address

\param[in]
  addressLength - length of address field;

\return
  checksum with address field
******************************************************************************/
static uint8_t srecWaitForAddress(uint8_t addressLength)
{
  uint8_t checksum = 0;

  while(addressLength)
  {
    addressLength--;
    UART1_GetByteWait( ((uint8_t *)&dataBuffer.address) + addressLength);
    checksum += *(((uint8_t *)&dataBuffer.address) + addressLength);
  }

  return checksum;
}

/**************************************************************************//**
\brief Communication support between host and mcu
******************************************************************************/
void srecProtocol(void)
{
	EnableWatchDog(WDP_8S);
	code_size = 0;
	
	while(1)
	{
		if (srecParser())
		{
			if ((S0 == dataBuffer.recordType) || (S5 == dataBuffer.recordType))
			{
				// copy filename to app info
				uint16_t length = VERSION_NAME_LENGTH < dataBuffer.dataSize ? VERSION_NAME_LENGTH : dataBuffer.dataSize;
				memcpy(app_info.version, dataBuffer.data, length);
				app_info.version[length] = 0;	// end of string at end o data
				UART1_SendData((uint8_t*)(&srecACK), sizeof(srecACK));
				continue;
			}

			if ((S7 == dataBuffer.recordType) || (S8 == dataBuffer.recordType) || (S9 == dataBuffer.recordType))
			{
				flashFlushPage();
				UART1_SendData((uint8_t*)(&srecACK), sizeof(srecACK));
				UART1_Uninit();
				DisableWatchDog();//LowLevelUninit();

				if (code_size & 0x0001)
					code_size += 1;

				code_size >>= 1;									// convert to words
				app_info.code_size = code_size;						// store code size
				app_info.crc16 = ComputeCRC16(app_info.code_size);	// calculate and store app crc
				app_info.invalid = VALID;							// mark application as valid
				storeAppInfo();										// store app
				JumpToAddress(dataBuffer.address);					// run application at start address
			}

			if (previousAddress == dataBuffer.address)
			{ // host did not listen ack and repeat previous packet
				UART1_SendData((uint8_t*)(&srecACK), sizeof(srecACK));
				continue;
			}
			previousAddress = dataBuffer.address;

			if ((dataBuffer.address + dataBuffer.dataSize) <= (FLASH_END_ADDRESS+1))
			{ // flash section
				// store last used addres in flash
				if (code_size < dataBuffer.address + dataBuffer.dataSize)
					code_size = dataBuffer.address + dataBuffer.dataSize;
				flashPacketToPage();
			}
			else if (dataBuffer.address < EEPROM_START_ADDRESS)
			{
				UART1_SendData((uint8_t*)(&srecNACK), sizeof(srecNACK));
				continue;
			}

			if ((dataBuffer.address >= EEPROM_START_ADDRESS) && (dataBuffer.address <= EEPROM_END_ADDRESS))
			{ // eeprom section
				eepromPacketToPage();
			}

			// send ack
			UART1_SendData((uint8_t*)(&srecACK), sizeof(srecACK));
			//UART1_SendAck();
		}
		else
		{
			UART1_SendData((uint8_t*)(&srecNACK), sizeof(srecNACK));
			//UART1_SendNack();
		}
	}
}

/**************************************************************************//**
\brief Real time parser of srec format.

\return
  false - parse error
  true  - parse ok
******************************************************************************/
static bool srecParser(void)
{
	uint8_t addressLength;
	uint8_t checksum = 0;
  
	// start of packet is transmitted as ascii code.
	// the others are transmitted as hex code.
	// search begin of packet
	srecWaitForType(&dataBuffer.recordType);

	switch (dataBuffer.recordType)
	{
		case S0: // the address field is 2 bytes
		case S1:
		case S5:
		case S9:
			addressLength = 2;
			break;
    
		case S2: // the address field is 3 bytes
		case S8:
			addressLength = 3;
			break;
    
		case S3: // the address field is 4 bytes
		case S7:
			addressLength = 4;
			break;
    
		default: // unsupported type
			return false;
	}

	// get packet length
	UART1_GetByteWait(&dataBuffer.dataSize);
	if (dataBuffer.dataSize > (SREC_DATA_LENGTH + addressLength + CHECKSUM_AREA_SIZE))
		return false; // this length is not correct
  
	checksum += dataBuffer.dataSize;
	dataBuffer.dataSize = dataBuffer.dataSize - addressLength - CHECKSUM_AREA_SIZE;

	// get internal address
	checksum += srecWaitForAddress(addressLength);


	// get data array including checksum byte
	for(uint8_t count=0;count<= dataBuffer.dataSize;count++)
	{
		UART1_GetByteWait(&dataBuffer.data[count]);
		checksum += dataBuffer.data[count];
	}

	if (0xFF != checksum)
		return false;

	return true;
}

/**************************************************************************//**
\brief Handshake procedure.
******************************************************************************/
void srecHandshake(uint8_t infinite)
{
	uint32_t rx_handshake = 0;
	
	UART1_SendStringPGM(PSTR("Waiting for download start ..."));
	
	// start timeru odpocitavani 1s
	timerIntervalInit();
	timerIntervalStart();
		
	while(!timerReadIntervalState() || infinite)
	{
		if ( UART1_GetByteNoWait((uint8_t*)&rx_handshake + 3) )
		{
			if (rx_handshake == HANDSHAKE_REQ) // nalezen pozadavek na nahrati fw
			{
				timerIntervalStop();
				timerIntervalUnInit();
				UART1_SendData((uint8_t *)(&handshakeConf), sizeof(handshakeConf)); // ack for handshake
				return;
			}
			
			rx_handshake >>= 8;
		}
	}

	UART1_SendStringPGM(PSTR("no request received, starting stored application\r\n"));
	
	timerIntervalStop();
	timerIntervalUnInit();
	UART1_Uninit();
	asm volatile ("jmp 0x0000" ::);
}

// eof srecParser.c
